package vn.edu.hut.soict.automaton;

import java.util.ArrayList;
import java.util.List;

public class State<T> {
	protected DFA<T> automaton;
	protected int id;
	protected boolean isFinal;
	protected List<Transition<T>> nextTransitions;
	protected List<Transition<T>> prevTransitions;

	public State() {
		isFinal = false;
		nextTransitions = new ArrayList<Transition<T>>();
		prevTransitions = new ArrayList<Transition<T>>();
	}

	public State(DFA<T> automaton, int id) {
		this();
		this.automaton = automaton;
		this.id = id;
	}

	public State(DFA<T> automaton, int id, boolean isFinal) {
		this(automaton, id);
		this.isFinal = isFinal;
	}

	public DFA<T> getAutomaton() {
		return automaton;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public boolean isFinal() {
		return isFinal;
	}

	public void setFinal(boolean isFinal) {
		this.isFinal = isFinal;
	}

	public boolean isStart() {
		return this == automaton.getStartState();
	}

	public List<Transition<T>> getNextTransitions() {
		return nextTransitions;
	}

	public List<Transition<T>> getPrevTransitions() {
		return prevTransitions;
	}

	public boolean hasChildren() {
		return nextTransitions.size() > 0;
	}

	public boolean isConfluence() {
		return prevTransitions.size() > 1;
	}

	public boolean equals(Object o) {
		/*
		 * Two states are equivalent if they are discriminative and
		 * their right languages are equivalent.
		 */
		if (!(o instanceof State)) {
			return false;
		}
		State state = (State) o;
		if (this.id != state.id) {
			return false;
		}
		if (this.isFinal != state.isFinal) {
			return false;
		}
		if (this.prevTransitions.size() != state.prevTransitions.size()) {
			return false;
		}
		if (this.nextTransitions.size() != state.nextTransitions.size()) {
			return false;
		}
		for (int i = 0; i < prevTransitions.size(); i++) {
			if (!this.prevTransitions.get(i).isEquivalent((Transition<T>) state.prevTransitions.get(i))) {
				return false;
			}
		}
		for (int i = 0; i < nextTransitions.size(); i++) {
			if (!this.nextTransitions.get(i).isEquivalent((Transition<T>) state.nextTransitions.get(i))) {
				return false;
			}
		}
		return true;
	}

	public boolean isEquivalent(State<T> state) {
		/*
		 * Two states are equivalent if they are discriminative and
		 * their right languages are equivalent.
		 */
		if (state == null) {
			return false;
		}
		if (this.id == state.id) {
			return false;
		}
		if (this.isFinal != state.isFinal) {
			return false;
		}
		if (this.nextTransitions.size() != state.nextTransitions.size()) {
			return false;
		}
		for (int i = 0; i < nextTransitions.size(); i++) {
			if (!this.nextTransitions.get(i).isEquivalent((Transition<T>) state.nextTransitions.get(i))) {
				return false;
			}
		}
		return true;
	}

	public String toString() {
		StringBuilder sb = new StringBuilder();
		sb.append(String.format("Node(id = %d)", id));
		if (isFinal) {
			sb.append(", final");
		}
		return sb.toString();
	}

	public int hashCode() {
		int sum = 0;
		for (int i = 0; i < nextTransitions.size(); i++) {
			sum += nextTransitions.get(i).getTarget().getId();
		}
		return sum;
	}
}